/**
 * 
 */
package org.codesketch.controller.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import org.codesketch.controller.dao.TaskRepository;
import org.codesketch.controller.domain.Status;
import org.codesketch.controller.domain.Task;
import org.codesketch.controller.domain.Test;
import org.codesketch.controller.service.ScheduleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

/**
 * @author Quirno Brizi
 *
 */
@Service
public class ScheduleServiceImpl implements ScheduleService {

	@Autowired
	private transient TaskRepository repository;
	
	private transient ScheduledExecutorService scheduler;
	private transient Map<String, ScheduledFuture<Test>> futures;
	
	@PostConstruct
	protected void init() {
		scheduler = Executors.newSingleThreadScheduledExecutor();
		futures = new HashMap<String, ScheduledFuture<Test>>();
	}
	
	/* (non-Javadoc)
	 * @see org.codesketch.controller.service.ScheduleService#schedule(java.lang.Long)
	 */
	@Override
	public Test schedule(final Long id) {
		Task task = repository.find(id);
		Test test;
		if(null!=task) {
			String uuid = UUID.randomUUID().toString();
			test = task.toTest();
			ScheduledFuture<Test> future = scheduler.schedule(
					new Executor(test), task.getDelay(), TimeUnit.MINUTES);
			test.setUuid(uuid);
			test.setStatus(Status.SCHEDULED);
			futures.put(uuid, future);
		} else {
			test = new Test();
		}
		return test;
	}

	/* (non-Javadoc)
	 * @see org.codesketch.controller.service.ScheduleService#cancel(java.lang.String)
	 */
	@Override
	public Test cancel(final String uuid) {
		ScheduledFuture<Test> future = futures.get(uuid);
		if(!future.isDone()) {
			future.cancel(true);
		}
		Test test = null;
		try {
			/*
			 * It is blocking run returning a future as response
			 */
			test = future .get();
			// TODO stop instances.
			test.setStatus(Status.CANCELLED);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			test = new Test();
			test.setStatus(Status.GENERIC_ERROR);
		}
		return test;
	}
	
	@Override
	public Set<Test> scheduled() {
		Set<Test> tests = new HashSet<Test>();
		return tests;
	}
	
	@Scheduled(fixedRate=10000)
	void check() {
		List<String> toRemove = new ArrayList<String>();
		for(final String uuid : futures.keySet()) {
			ScheduledFuture<Test> future = futures.get(uuid);
			if(future.isDone()) {
				/*
				 * Test deploy done check if still executing
				 */
				try {
					Test test = future.get();
					if(Status.RUNNING.equals(test.getStatus())) {
						/*
						 * Test running verify still processing
						 */
						Integer duration = test.getDuration() * 60 * 1000;
						Long startTime = test.getStartTime();
						Long now = System.currentTimeMillis();
						if((now - startTime) > duration) {
							/*
							 * Test window expired
							 */
							toRemove.add(uuid);
						}
					} else {
						/*
						 * Test not running may be cancelled or failure
						 */
						toRemove.add(uuid);
					}
				} catch (Exception e) {
					toRemove.add(uuid);
					continue;
				} 
			}
		}
		for(String uuid : toRemove) {
			futures.remove(uuid);
		}
	}
}
